﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Threading.Tasks;

namespace MageWars.Support
{
    public class RectProperties
    {
        public bool IsTransitable { get; set; }
    }

    public class Mesh
    {
        private List<Rectangle> RectMesh;

        public Dictionary<Rectangle, RectProperties> RectProperties { get; set; }

        private static Mesh Graph;

        private IEnumerable<Rectangle> Picar2(Rectangle Current)
        {

            int minSize = 15;

            Rectangle Vr1;
            Rectangle Vr2;
            Rectangle Hr1;
            Rectangle Hr2;

            bool Vr1Valido = false;
            bool Vr2Valido = false;
            bool Hr1Valido = false;
            bool Hr2Valido = false;

            //picando mitad vertical
            int mitadV = Current.Width / 2;

            Vr1 = new Rectangle(Current.X, Current.Y, mitadV, Current.Height);
            Vr2 = new Rectangle(Current.X + mitadV, Current.Y, Current.Width % 2 != 0 ? mitadV + 1 : mitadV, Current.Height);

            Vr1Valido = Collisions.RectanguloCubreUnSoloColor(Vr1);
            Vr2Valido = Collisions.RectanguloCubreUnSoloColor(Vr2);


            int mitadH = Current.Height / 2;

            Hr1 = new Rectangle(Current.X, Current.Y, Current.Width, mitadH);
            Hr2 = new Rectangle(Current.X, Current.Y + mitadH, Current.Width, Current.Height % 2 != 0 ? mitadH + 1 : mitadH);

            Hr1Valido = Collisions.RectanguloCubreUnSoloColor(Hr1);
            Hr2Valido = Collisions.RectanguloCubreUnSoloColor(Hr2);

            bool CurrentValido = Collisions.RectanguloCubreUnSoloColor(Current);


            List<Rectangle> rr = new List<Rectangle>();

            if (CurrentValido)
            {
                if (Current.Height > 10 * minSize)
                {
                    rr.AddRange(Picar2(Hr1));
                    rr.AddRange(Picar2(Hr2));
                    return rr;
                }
                else if (Current.Width > 10 * minSize)
                {
                    rr.AddRange(Picar2(Vr1));
                    rr.AddRange(Picar2(Vr2));
                    return rr;
                }
                else
                {
                    return new Rectangle[] { Current };
                }
            }

            if (Current.Width <= minSize && Current.Height > minSize)
            {
                rr.AddRange(Picar2(Hr1));
                rr.AddRange(Picar2(Hr2));
                return rr;

            }
            else if (Current.Height <= minSize && Current.Width > minSize)
            {
                rr.AddRange(Picar2(Vr1));
                rr.AddRange(Picar2(Vr2));
                return rr;
            }
            else if (Current.Height <= minSize && Current.Width <= minSize)
            {
                return new Rectangle[] { Current };
            }



            if (Vr1Valido && Vr2Valido)
            {
                rr.AddRange(Picar2(Vr1));
                rr.AddRange(Picar2(Vr2));
            }
            else if (Hr1Valido && Hr2Valido)
            {
                rr.AddRange(Picar2(Hr1));
                rr.AddRange(Picar2(Hr2));
            }
            else if (Vr1Valido && !Vr2Valido)
            {
                rr.AddRange(Picar2(Vr1));
                rr.AddRange(Picar2(Vr2));
            }
            else if (Vr2Valido && !Vr1Valido)
            {
                rr.AddRange(Picar2(Vr2));
                rr.AddRange(Picar2(Vr1));
            }
            else if (Hr1Valido && !Hr2Valido)
            {
                rr.AddRange(Picar2(Hr1));
                rr.AddRange(Picar2((Hr2)));
            }

            else if (Hr2Valido && !Hr1Valido)
            {
                rr.AddRange(Picar2(Hr2));
                rr.AddRange(Picar2(Hr1));
            }
            else if (Current.Width >= Current.Height)
            {
                rr.AddRange(Picar2(Vr1));
                rr.AddRange(Picar2(Vr2));
            }
            else
            {
                rr.AddRange(Picar2(Hr1));
                rr.AddRange(Picar2(Hr2));
            }

            return rr;
        }

        private void CreateMesh()
        {
            var rr = new List<Rectangle>();
            rr.AddRange(Picar2(new Rectangle(0, 0, 1180, 700)));
            RectMesh = rr;

            foreach (var x in RectMesh)
            {
                if (!Collisions.RectanguloCubreNegro(x))
                {
                    Graph.CreateNode(x);
                    RectProperties.Add(x, new RectProperties() { IsTransitable = true });
                }
                else
                {
                    RectProperties.Add(x, new RectProperties() { IsTransitable = false });
                }
            }

            for (int i = 0; i < Graph.Nodes.Count; i++)
            {
                var aa = Graph.Nodes[i].Rectangle;
                aa.Inflate(1, 1);
                var node = Graph.Nodes[i];
                for (int j = i + 1; j < Graph.Nodes.Count; j++)
                {
                    if (aa.Intersects(Graph.Nodes[j].Rectangle))
                    {
                        var node2 = Graph.Nodes[j];
                        Node.AddConnection(node, node2);
                    }
                }
            }
        }

        public static Mesh GetMesh()
        {
            if (Graph == null)
            {
                Graph = new Mesh();
                Graph.CreateMesh();
            }
            return Graph;
        }

        private Mesh()
        {
            Nodes = new List<Node>();
            RectNodes = new Dictionary<Rectangle, Node>();
            RectProperties = new Dictionary<Rectangle, RectProperties>();
        }

        public List<Node> Nodes { get; set; }

        public Dictionary<Rectangle, Node> RectNodes { get; set; }

        private Node CreateNode(Rectangle r)
        {
            var g = new Node();
            g.Position = new Point(r.Center.X, r.Center.Y);
            g.Rectangle = r;
            Nodes.Add(g);
            RectNodes.Add(r, g);
            return g;
        }

        private static Dictionary<Node, NodoAStar> Nodos = new Dictionary<Node, NodoAStar>();

        public IEnumerable<Node> Search(Node start, Node Destination)
        {
            if (Destination == null)
            {
                return new Node[] { start };
            }
            if (Nodos.Count != this.Nodes.Count)
            {
                foreach (var x in this.Nodes)
                    Nodos.Add(x, new NodoAStar(x));
            }
            else
            {
                foreach (var x in Nodos)
                    x.Value.Reset();
            }

            C5.IntervalHeap<NodoAStar> OpenNodes = new C5.IntervalHeap<NodoAStar>(new NodoAStarComparer());
            NodoAStar CurrentNode = null;
            OpenNodes.Add(Nodos[start]);
            while (true)
            {

                CurrentNode = OpenNodes.DeleteMin();
                if (CurrentNode.Nodo == Destination)
                {
                    break;
                }
                else
                {
                    CurrentNode.Closed = true;
                    foreach (var x in CurrentNode.Nodo.Nodes)
                    {
                        NodoAStar NN = Nodos[x.Key];
                        NN.Heuristica = Node.Distance(NN.Nodo, Destination);
                        var costo = CurrentNode.Costo + x.Value;

                        if (!NN.Closed && !NN.Opened)
                        {
                            NN.Padre = CurrentNode;
                            NN.Costo = costo;
                            NN.Opened = true;
                            OpenNodes.Add(NN);
                        }
                        else if (costo < NN.Costo)
                        {
                            NN.Costo = costo;
                            NN.Padre = CurrentNode;
                        }
                    }
                }
            }

            List<Node> Camino = new List<Node>();
            NodoAStar n = CurrentNode;
            while (n.Nodo != start)
            {
                Camino.Add(n.Nodo);
                n = n.Padre;
            }

            Camino.Add(n.Nodo);
            Camino.Reverse();
            return Camino;
        }
    }

    public class NodoAStarComparer : IComparer<NodoAStar>
    {
        int IComparer<NodoAStar>.Compare(NodoAStar x, NodoAStar y)
        {
            return (int)(x.Estimado - y.Estimado);
        }
    }

    public class NodoAStar
    {
        public bool Closed { get; set; }
        public Node Nodo { get; set; }
        public NodoAStar Padre { get; set; }
        public double Costo { get; set; }
        public double Heuristica { get; set; }
        public double Estimado { get { return Costo + Heuristica; } }
        public bool Opened { get; set; }

        public void Reset()
        {
            this.Padre = this;
            this.Costo = 0;
            this.Closed = false;
            this.Opened = false;
            this.Heuristica = 0;
        }

        public override int GetHashCode()
        {
            return (int)(Nodo.Position.X * 123 / Nodo.Position.Y);
        }

        public NodoAStar(Node Nodo)
        {
            this.Nodo = Nodo;
            this.Padre = this;
        }

        public override string ToString()
        {
            return this.Nodo.ToString();
        }

    }

    public class Node
    {
        public override string ToString()
        {
            return Position.ToString();
        }

        protected internal Node()
        {
            Nodes = new Dictionary<Node, double>();
        }

        public Rectangle Rectangle { get; set; }

        public Point Position { get; set; }

        public Dictionary<Node, double> Nodes { get; private set; }

        public static double Distance(Node n1, Node n2)
        {
            return Math.Pow(n2.Position.X - n1.Position.X, 2) + Math.Pow(n2.Position.Y - n1.Position.Y, 2);
        }

        public static void AddConnection(Node n1, Node n2)
        {
            double costo = Node.Distance(n1, n2);
            n1.Nodes.Add(n2, costo);
            n2.Nodes.Add(n1, costo);
        }

    }
}
